#pragma once
#include<any>
#include"../Buffer/Buffer.hpp"
#include"../Channel/Channel.hpp"
#include"../Log/log.hpp"
#include"../Socket/Socket.hpp"

    typedef enum {
        CONNECTING,//半连接
        CONNECTED,//连接
        DISCONNECTING,//半关闭
        DISCONNECTED//关闭
    }Statu;

    class Connection;
    using ConnectPtr = std::shared_ptr<Connection>;

    class Connection:public std::enable_shared_from_this<Connection> {
    private:
        int _sockfd;//套接字描述符
        Socket _socket;//套接字操作管理
        uint64_t _conn_fd;//连接类的描述符，具有唯一性。与时间轮的描述符可以共用
        Channel _channel;//描述符事件监控模块
        EventLoop* _loop;
        Statu _statu;//连接状态
        Buffer _in_buffer;//输入缓冲区，存储接收的数据
        Buffer _out_buffer;//输出缓冲区，存储要发出的数据
        std::any _context;//请求的接收处理上下文
        bool _inactive_release;//是否使用非活跃销毁，默认为false不使用
        
        //事件回调函数
        using ConnectedCallback = std::function<void(const ConnectPtr&)>;
        using MessageCallback = std::function<void(const ConnectPtr&, Buffer*)>;
        using CloseCallback = std::function<void(const ConnectPtr&)>;
        using EventCallback = std::function<void(const ConnectPtr&)>;
        ConnectedCallback _connected_callback;//连接后的回调
        MessageCallback _message_callback;//收到数据后的业务处理回调
        CloseCallback _close_callback;//关闭连接的回调
        EventCallback _event_callback;//任意事件的回调

        /*组件内的连接关闭回调--组件内设置的，因为服务器组件内会把所有的连接管理起来，一旦某个连接要关闭*/
        /*就应该从管理的地方移除掉自己的信息*/
        CloseCallback _server_close_callback;
    private:
        //channel的五个回调：

        //描述符可读事件触发后调用的函数，接收socket数据放到接收缓冲区中，然后调用_message_callback
        void HandleRead()
        {
            char buf[65536];
            //从socket中接收数据
            int ret = _socket.RecvNonblock(buf, 65535);
            if(ret < 0)
            {
                //出错了，但不是直接关闭，要做处理
                Close();
                return ;
            }
            DBG_LOG("ret = %d\n",ret);
            _in_buffer.write(buf, ret);
            if(_in_buffer.get_readable_size() > 0)
            {
                //调用业务处理回调
                _message_callback(shared_from_this(), &_in_buffer);
            }
        }

        //描述符可写事件触发后调用的函数，将发送缓冲区中的数据进行发送
        void HandleWrite()
        {
            int ret = _socket.SendNonblock(_out_buffer.get_read_ptr(), _out_buffer.get_readable_size());
            if(ret < 0)
            {
                //出错就要关闭
                if(_in_buffer.get_readable_size() > 0)
                {
                    //调用业务处理回调
                    _message_callback(shared_from_this(), &_in_buffer);
                }
                return ReleaseInLoop();
            }
            //移动缓冲区读指针
            _out_buffer.move_read_pos(ret);
            if(_out_buffer.get_readable_size() == 0)
            {
                //没有数据可读，就关闭写监控
                _channel.DisableWrite();
                if(_statu == DISCONNECTING)//可能是在Close接口执行后，调用的写回调，所以要判断
                {
                    //半关闭状态，就直接释放
                    ReleaseInLoop();
                }
            }
        }

        //描述符触发关闭事件
        void HandleClose()
        {
            //触发关闭后，套接字什么都不干了，接收缓冲区有数据就处理一下
            if(_in_buffer.get_readable_size() > 0)
            {
                _message_callback(shared_from_this(), &_in_buffer);
            }
            ReleaseInLoop();
        }
        
        //描述符触发错误事件  
        void HandleError()
        {   
            HandleClose();
        }

        //描述符触发任意事件: 1. 刷新连接的活跃度--延迟定时销毁任务；  2. 调用组件使用者的任意事件回调
        void HandleEvent()
        {
            if(_inactive_release == true)
            {
                _loop->FlushTimer(_conn_fd);
            }
            if(_event_callback)
            {
                _event_callback(shared_from_this());
            }
        }
        
        //这个关闭操作并非实际的连接释放操作，需要判断还有没有数据待处理，待发送
        void Close()
        {
            //1.修改连接状态
            _statu = DISCONNECTING;
            //2.有无数据要处理
            if(_in_buffer.get_readable_size() > 0)
            {
                _message_callback(shared_from_this(), &_in_buffer);
            }
            //3.有无数据要发送
            //两种情况：1.无数据要发送 2.HandleRead写入数据出错,就打开写
            if(_out_buffer.get_readable_size() == 0)
            {
                ReleaseInLoop();
            }
            if(_out_buffer.get_readable_size() > 0)
            {
                if(_channel.Writeable() == false)
                    _channel.EnableWrite();
            }
        }

        void Release()
        {
            //1.修改连接状态
            _statu = DISCONNECTED;
            //2.删除描述符监控
            _channel.RemoveAll();
            //3.关闭套接字
            _socket.Close();
            //4.如果定时器还有销毁任务，就取消任务
            if(_loop->HasTimer(_conn_fd)) 
                CloseInactiveRelease();
            //5.调用用户的关闭连接回调
            if(_close_callback != nullptr)
                _close_callback(shared_from_this());
            //6.调用服务器的关闭连接回调,在用户关闭回调之后调用，是因为先执行服务器的关闭回调会销毁Connection，就无法调用用户回调了
            if(_server_close_callback != nullptr)
                _server_close_callback(shared_from_this());
        }

        //建立连接后，要对连接进行各种设置，才能算是进入CONNECTED状态
        void Established()
        {
            //1.修改连接状态
            //ERR_LOG("---------%d", _statu);
            assert(_statu == CONNECTING);
            _statu = CONNECTED;
            //2.启动读事件监控
            _channel.EnableRead();
            //3.调用回调函数
            if(_connected_callback != nullptr)
            {
                _connected_callback(shared_from_this());
            }
        }

        //并不是真正的发送数据，而是将数据放到发送缓冲区，开启写监控
        void Send(Buffer& buf)
        {
            if(_statu == DISCONNECTED)
                return;

            _out_buffer.Buffer_write(buf);


            if(_channel.Writeable() == false)
            {
                _channel.EnableWrite();
            }
        }

        //启动非活跃销毁，规定超时时间
        void EnableInactiveRelease(size_t sec)
        {
            //1.将非活跃销毁标志位设置为true
            _inactive_release = true;
            //2.如果销毁任务已存在，就刷新一下
            if(_loop->HasTimer(_conn_fd))
            {
                _loop->FlushTimer(_conn_fd);
            }
            //3.如果销毁任务不存在，就增加销毁任务
            else
            {
                _loop->AddTimer(_conn_fd, sec, std::bind(&Connection::ReleaseInLoop, this));
            }
        }
        //关闭非活跃销毁
        void CloseInactiveRelease()
        {
            //1.将非活跃销毁标志位设置为true
            _inactive_release = false;
            //2.如果销毁任务存在，就关闭任务
            if(_loop->HasTimer(_conn_fd))
            {
                _loop->CancelTimer(_conn_fd);
            }
        }

        void Update(const std::any& context, 
        const ConnectedCallback& connect_callback, 
        const MessageCallback& message_callback,
        const CloseCallback& close_callback,
        const EventCallback& event_callback)
        {
            _context = context;
            _connected_callback = connect_callback;
            _message_callback = message_callback;
            _close_callback = close_callback;
            _event_callback = event_callback; 
        }
    public:
        Connection(EventLoop* loop, uint64_t conn_fd, int sockfd)
        :_sockfd(sockfd)
        ,_conn_fd(conn_fd)
        ,_loop(loop)
        ,_statu(CONNECTING)
        ,_socket(sockfd)
        ,_channel(sockfd, loop)
        ,_inactive_release(false)
        {
            _channel.set_read_callback(std::bind(&Connection::HandleRead, this));
            _channel.set_write_callback(std::bind(&Connection::HandleWrite, this));
            _channel.set_close_callback(std::bind(&Connection::HandleClose, this));
            _channel.set_error_callback(std::bind(&Connection::HandleError, this));
            _channel.set_event_callback(std::bind(&Connection::HandleEvent, this));
        }

        ~Connection()
        {
            DBG_LOG("Connnection:%p destory", this);
        }

        //获取管理的文件描述符
        int GetSockfd() { return _sockfd; }
        //获取连接ID
        int GetConnfd() { return _conn_fd; }
        //是否处于CONNECTED状态
        bool Connected() { return (_statu == CONNECTED); }
        //设置上下文--连接建立完成时进行调用
        void SetContext(const std::any &context) { _context = context; }
        //获取上下文，返回的是指针
        std::any *GetContext() { return &_context; }

        void SetConnectedCallback(const ConnectedCallback&cb) { _connected_callback = cb; }
        void SetMessageCallback(const MessageCallback&cb) { _message_callback = cb; }
        void SetCloseCallback(const CloseCallback&cb) { _close_callback = cb; }
        void SetEventCallback(const EventCallback&cb) { _event_callback = cb; }
        void SetSrvCloseCallback(const CloseCallback&cb) { _server_close_callback = cb; }
        //完成连接的基本设置
        void EstablishedInLoop()
        {
            _loop->RunInLoop(std::bind(&Connection::Established, this));
        }

        //发送数据
        void SendInLoop(const char* data, uint64_t len)
        {
            //因为data可能是一个临时数据，压入任务池后，当要是执行任务的时候，可能临时数据已经被释放了，所以先建立一个Buffer对象存储
            Buffer buf;
            buf.write(data, len);
            _loop->RunInLoop(std::bind(&Connection::Send, this, buf));
        }

        //关闭连接,但并不是真正关闭，要判断是否有数据未处理
        void CloseInLoop()
        {
            _loop->RunInLoop(std::bind(&Connection::Close, this));
        }

        //真正关闭连接
        void ReleaseInLoop()
        {
            _loop->PushInLoop(std::bind(&Connection::Release, this));
        }

        //启动非活跃销毁，规定超时时间
        void EnableInactiveReleaseInLoop(size_t sec)
        {
            _loop->RunInLoop(std::bind(&Connection::EnableInactiveRelease, this, sec));
        }
        //关闭非活跃销毁
        void CloseInactiveReleaseInLoop()
        {
            _loop->RunInLoop(std::bind(&Connection::CloseInactiveRelease, this));
        }

        //重置连接的上下文和回调函数
        //这个函数必须在EventLoop中立即执行，否则可能导致处理数据时，重置的任务还在任务池中，依旧使用旧回调处理
        void UpdateInLoop(const std::any& context, 
        const ConnectedCallback& connect_callback, 
        const MessageCallback& message_callback,
        const CloseCallback& close_callback,
        const EventCallback& event_callback)
        {
            //要保证立即执行，就要保证函数调用是就在EventLoop的线程中,因为RunInLoop的任务如果在这个线程中，就会直接执行
            _loop->AssertInLoop();
            _loop->RunInLoop(std::bind(&Connection::Update, this, context, connect_callback, message_callback, close_callback, event_callback));
        }
    };